#include <iostream>
#include "cbhelper.h"

#include "stlfutils.h"
#include "depsearch.h"

CDependencyRecord::CDependencyRecord( void ) {
  Clear();
}

CDependencyRecord::~CDependencyRecord( void ) {
  Clear();
}

void CDependencyRecord::SetName( const CString& Name ) {
  m_Name = Name;
  m_Hash = sdbm_hash( ( unsigned char * )Name.GetCString(), Name.GetLength() );
}

void CDependencyRecord::Clear( void ) {
  m_Hash = 0;
  m_Name.Clear();
  m_Dependencies.clear();
  m_CrossReferences.clear();
  m_DependenciesComplete = false;
  m_CrossReferencesComplete = false;
  m_Marker = 0;
}

void CDependencyRecord::Show( void ) {
  std::cout << "Unit: " << m_Name.GetCString() << std::endl;
  if( m_Dependencies.size() > 0 ) {
    std::cout << "Unit dependens on " << m_Dependencies.size() << " other units." << std::endl;
    for( size_t i = 0; i < m_Dependencies.size(); i++ ) {
      CDependencyRecord *r = m_Dependencies[i];
      std::cout << "Dependency #" << ( i + 1 ) << ": " << r->GetName().GetCString() << std::endl;
    }
  } else {
    std::cout << "Unit has no dependencies." << std::endl;
  }
  if( m_CrossReferences.size() > 0 ) {
    std::cout << "Unit is used by" << m_CrossReferences.size() << " other units." << std::endl;
    for( size_t i = 0; i < m_CrossReferences.size(); i++ ) {
      CDependencyRecord *r = m_CrossReferences[i];
      std::cout << "Cross reference #" << ( i + 1 ) << ": " << r->GetName().GetCString() << std::endl;
    }
  } else {
    std::cout << "Unit is not used by any other unit." << std::endl;
  }
}

size_t CDependencyRecord::DependenciesCount( void ) const {
  return m_Dependencies.size();
}

CDependencyRecord *CDependencyRecord::Dependency( const size_t Index ) const {
  if( Index < m_Dependencies.size() ) {
    return m_Dependencies[Index];
  }
  return 0;
}

void CDependencyRecord::AddDependency( const CDependencyRecord *Record ) {
  for( size_t i = 0; i < m_Dependencies.size(); i++ ) {
    if( Record == m_Dependencies[i] ) {
      return;
    }
  }
  m_Dependencies.push_back( ( CDependencyRecord * )Record );
}

size_t CDependencyRecord::CrossReferencesCount( void ) const {
  return m_CrossReferences.size();
}

CDependencyRecord *CDependencyRecord::CrossReference( const size_t Index ) const {
  if( Index < m_CrossReferences.size() ) {
    return m_CrossReferences[Index];
  }
  return 0;
}

void CDependencyRecord::AddCrossReference( const CDependencyRecord *Record ) {
  for( size_t i = 0; i < m_CrossReferences.size(); i++ ) {
    if( Record == m_CrossReferences[i] ) {
      return;
    }
  }
  m_CrossReferences.push_back( ( CDependencyRecord * )Record );
}


CDependencyInfo::CDependencyInfo( void ) {
  Clear();
}

CDependencyInfo::~CDependencyInfo( void ) {
  Clear();
}

void CDependencyInfo::Clear( void ) {
  for( size_t i = 0; i < m_Records.size(); i++ ) {
    delete m_Records[i];
  }
  m_Records.clear();
  m_Platform.Clear();
}

void CDependencyInfo::Show( void ) {
  if( m_Records.size() > 0 ) {
    std::cout << "Dependency info collected for " << m_Records.size() << " units." << std::endl;
    std::cout << "Unit Dependencies: Direct\tIndirect\tAll\t"
              << "XRefs: Direct\tIndirect\tAll\tPath" << std::endl;
    for( size_t i = 0; i < m_Records.size(); i++ ) {
      CDependencyRecord *r = m_Records[i];
      std::cout << "#" << ( i + 1 ) << "\t\t" << r->DependenciesCount();
      if( r->DependenciesComplete() ) {
        std::cout << "*";
      }
      std::cout << "\t\t" << IndirectDependenciesCount( i ) << "\t\t"
                << AllDependenciesCount( i ) << "\t" << r->CrossReferencesCount();
      if( r->CrossReferencesComplete() ) {
        std::cout << "*";
      }
      std::cout << "\t\t" << IndirectCrossReferencesCount( i ) << "\t\t"
                << AllCrossReferencesCount( i ) << "\t" << r->GetName().GetCString() << std::endl;
    }
  } else {
    std::cout << "Dependency information is not available." << std::endl;
  }
}

CString CDependencyInfo::OneLineReport( const size_t Index, const bool Deps, const bool XRefs ) {
  CString result;
  if( Index < m_Records.size() ) {
    CDependencyRecord *r = m_Records[Index];
    if( Deps ) {
      result += "Deps [" + IntegerToString( r->DependenciesCount() )
                + "/" + IntegerToString( IndirectDependenciesCount( Index ) )
                + "/" + IntegerToString( AllDependenciesCount( Index ) ) + "]";
    }
    if( Deps && XRefs ) {
      result += " ";
    }
    if( XRefs ) {
      result += " XRefs [" + IntegerToString( r->CrossReferencesCount() )
                + "/" + IntegerToString( IndirectCrossReferencesCount( Index ) )
                + "/" + IntegerToString( AllCrossReferencesCount( Index ) ) + "]";
    }
    result += " (direct/indirect/all)";
  }
  return result;
}

void CDependencyInfo::ResetMarkers( void ) {
  for( size_t i = 0; i < m_Records.size(); i++ ) {
    m_Records[i]->Marker() = 0;
  }
}

size_t CDependencyInfo::DependenciesCount( CDependencyRecord *Record ) {
  size_t result = 0;
  if( 0 != Record ) {
    result += Record->DependenciesCount();
    Record->Marker()++;
    for( size_t i = 0, n = Record->DependenciesCount(); i < n; i++ ) {
      if( 0 == Record->Dependency( i )->Marker() ) {
        result += DependenciesCount( Record->Dependency( i ) );
        Record->Dependency( i )->Marker()++;
      }
    }
  }
  return result;
}

CStringList CDependencyInfo::Dependencies( CDependencyRecord *Record ) {
  CStringList result;
  if( 0 != Record ) {
    result.Insert( Record->GetName() );
    Record->Marker()++;
    for( size_t i = 0, n = Record->DependenciesCount(); i < n; i++ ) {
      if( 0 == Record->Dependency( i )->Marker() ) {
        result.Insert( Dependencies( Record->Dependency( i ) ) );
        Record->Dependency( i )->Marker()++;
      }
    }
  }
  return result;
}

size_t CDependencyInfo::CrossReferencesCount( CDependencyRecord *Record ) {
  size_t result = 0;
  if( 0 != Record ) {
    result += Record->CrossReferencesCount();
    Record->Marker()++;
    for( size_t i = 0, n = Record->CrossReferencesCount(); i < n; i++ ) {
      if( 0 == Record->CrossReference( i )->Marker() ) {
        result += CrossReferencesCount( Record->CrossReference( i ) );
        Record->CrossReference( i )->Marker()++;
      }
    }
  }
  return result;
}

CStringList CDependencyInfo::CrossReferences( CDependencyRecord *Record ) {
  CStringList result;
  if( 0 != Record ) {
    result.Insert( Record->GetName() );
    Record->Marker()++;
    for( size_t i = 0, n = Record->CrossReferencesCount(); i < n; i++ ) {
      if( 0 == Record->CrossReference( i )->Marker() ) {
        result.Insert( CrossReferences( Record->CrossReference( i ) ) );
        Record->CrossReference( i )->Marker()++;
      }
    }
  }
  return result;
}

size_t CDependencyInfo::RecordsCount( void ) const {
  return m_Records.size();
}

size_t CDependencyInfo::DirectDependenciesCount( const size_t Index ) const {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    result += m_Records[Index]->DependenciesCount();
  }
  return result;
}

size_t CDependencyInfo::IndirectDependenciesCount( const size_t Index ) {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->DependenciesCount(); i < n; i++ ) {
      result += DependenciesCount( r->Dependency( i ) );
    }
  }
  return result;
}

size_t CDependencyInfo::AllDependenciesCount( const size_t Index ) {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    return DependenciesCount( m_Records[Index] );
  }
  return result;
}

CString CDependencyInfo::Name( const size_t Index ) const {
  if( Index < m_Records.size() ) {
    return m_Records[Index]->GetName();
  }
  return "";
}

CStringList CDependencyInfo::DirectDependencies( const size_t Index ) const {
  CStringList result;
  if( Index < m_Records.size() ) {
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->DependenciesCount(); i < n; i++ ) {
      result.Insert( r->Dependency( i )->GetName() );
    }
  }
  return result;
}

CStringList CDependencyInfo::IndirectDependencies( const size_t Index ) {
  CStringList result;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->DependenciesCount(); i < n; i++ ) {
      const CDependencyRecord *rr = r->Dependency( i );
      for( size_t j = 0, m = rr->DependenciesCount(); j < m; j++ ) {
        result.Insert( Dependencies( rr->Dependency( j ) ) );
      }
    }
  }
  return result;
}

CStringList CDependencyInfo::AllDependencies( const size_t Index ) {
  CStringList result;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->DependenciesCount(); i < n; i++ ) {
      result.Insert( Dependencies( r->Dependency( i ) ) );
    }
  }
  return result;
}

size_t CDependencyInfo::DirectCrossReferencesCount( const size_t Index ) const {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    result += m_Records[Index]->CrossReferencesCount();
  }
  return result;
}

size_t CDependencyInfo::IndirectCrossReferencesCount( const size_t Index ) {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->CrossReferencesCount(); i < n; i++ ) {
      result += CrossReferencesCount( r->CrossReference( i ) );
    }
  }
  return result;
}

size_t CDependencyInfo::AllCrossReferencesCount( const size_t Index ) {
  size_t result = 0;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    return CrossReferencesCount( m_Records[Index] );
  }
  return result;
}

CStringList CDependencyInfo::DirectCrossReferences( const size_t Index ) const {
  CStringList result;
  if( Index < m_Records.size() ) {
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->CrossReferencesCount(); i < n; i++ ) {
      result.Insert( r->CrossReference( i )->GetName() );
    }
  }
  return result;
}

CStringList CDependencyInfo::IndirectCrossReferences( const size_t Index ) {
  CStringList result;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->CrossReferencesCount(); i < n; i++ ) {
      const CDependencyRecord *rr = r->CrossReference( i );
      for( size_t j = 0, m = rr->CrossReferencesCount(); j < m; j++ ) {
        result.Insert( CrossReferences( rr->CrossReference( j ) ) );
      }
    }
  }
  return result;
}

CStringList CDependencyInfo::AllCrossReferences( const size_t Index ) {
  CStringList result;
  if( Index < m_Records.size() ) {
    ResetMarkers();
    const CDependencyRecord *r = m_Records[Index];
    for( size_t i = 0, n = r->CrossReferencesCount(); i < n; i++ ) {
      result.Insert( CrossReferences( r->CrossReference( i ) ) );
    }
  }
  return result;
}

bool CDependencyInfo::AreDependenciesComplete( const size_t Index ) {
  if( Index < m_Records.size() ) {
    return m_Records[Index]->DependenciesComplete();
  }
  return false;
}

bool CDependencyInfo::AreCrossReferencesComplete( const size_t Index ) {
  if( Index < m_Records.size() ) {
    return m_Records[Index]->CrossReferencesComplete();
  }
  return false;
}

void CDependencyInfo::SetDependenciesComplete( const size_t Index, const bool State ) {
  if( Index < m_Records.size() ) {
    m_Records[Index]->DependenciesComplete() = State;
  }
}

void CDependencyInfo::SetCrossReferencesComplete( const size_t Index, const bool State ) {
  if( Index < m_Records.size() ) {
    m_Records[Index]->CrossReferencesComplete() = State;
  }
}

int CDependencyInfo::FindRecord( const CString& Name ) {
  const hash_t h = sdbm_hash( ( unsigned char * )Name.GetCString(), Name.GetLength() );
  for( size_t i = 0; i < m_Records.size(); i++ ) {
    CDependencyRecord *r = m_Records[i];
    if( r->Hash() == h ) {
      if( r->GetName() == Name ) {
        return i;
      }
    }
  }
  return -1;
}

size_t CDependencyInfo::AddRecord( const CString& Name ) {
  int Index = FindRecord( Name );
  if( Index >= 0 ) {
    return Index;
  } else {
    CDependencyRecord *r = new CDependencyRecord;
    r->SetName( Name );
    m_Records.push_back( r );
    Index = m_Records.size() - 1;
  }
  return Index;
}

bool CDependencyInfo::AddDependency( const size_t Index, const CString& DependencyName ) {
  if( Index < m_Records.size() ) {
    size_t dep_index = AddRecord( DependencyName );
    CDependencyRecord *r = m_Records[Index];
    CDependencyRecord *d = m_Records[dep_index];
    r->AddDependency( d );
    d->AddCrossReference( r );
    return true;
  }
  return false;
}

size_t CDependencyInfo::AddDependency( const CString& Name, const CString& DependencyName ) {
  size_t rec_index = AddRecord( Name );
  size_t dep_index = AddRecord( DependencyName );
  CDependencyRecord *r = m_Records[rec_index];
  CDependencyRecord *d = m_Records[dep_index];
  r->AddDependency( d );
  d->AddCrossReference( r );
  return rec_index;
}

void CDependencyInfo::MakeRules( CMakefile& Makefile, const int Section, const bool Multiline ) {
  for( size_t i = 0; i < m_Records.size(); i++ ) {
    const CDependencyRecord *r = m_Records[i];
    CStringList deps( DirectDependencies( i ) );
    if( deps.GetCount() > 0 ) {
      deps.RemoveDuplicates();
      CMakefileRule& rule = Makefile.AddRule( Platform().Pd( r->GetName() ), Section );
      rule.Multiline() = Multiline;
      for( int j = 0; j < deps.GetCount(); j++ ) {
        rule.Dependencies().Insert( Platform().Pd( deps[j] ) );
      }
    }
  }
}


CIncludeSearchFilter::CIncludeSearchFilter( void ) {
  Clear();
}

CIncludeSearchFilter::CIncludeSearchFilter( const CIncludeSearchFilter& Filter ) {
  Assign( Filter );
}

CIncludeSearchFilter::~CIncludeSearchFilter( void ) {
  Clear();
}

void CIncludeSearchFilter::Clear( void ) {
  m_IncludeDirectories.Clear();
  m_DefinedMacros.Clear();
}

void CIncludeSearchFilter::Show( void ) {
  //
}

void CIncludeSearchFilter::Assign( const CIncludeSearchFilter& Filter ) {
  m_IncludeDirectories = Filter.m_IncludeDirectories;
  m_DefinedMacros = Filter.m_DefinedMacros;
}

bool CIncludeSearchFilter::Execute( const CString& FileName, CStringList& Includes ) {
  ( void )FileName;
  ( void )Includes;
  return false;
}

bool CIncludeSearchFilter::Execute( const CString& FileName, CDependencyInfo& Dependencies ) {
  ( void )FileName;
  ( void )Dependencies;
  return false;
}

void CIncludeSearchFilter::AddIncludeDirectory( const CString& Path ) {
  m_IncludeDirectories.Insert( Path );
}

void CIncludeSearchFilter::AddIncludeDirectories( const CStringList& Paths ) {
  m_IncludeDirectories.Insert( Paths );
}

void CIncludeSearchFilter::AddMacroDefiniton( const CString& Macro ) {
  m_DefinedMacros.Insert( Macro );
}

void CIncludeSearchFilter::AddMacroDefinitons( const CStringList& Macros ) {
  m_DefinedMacros.Insert( Macros );
}

CString CIncludeSearchFilter::ResolveIncludePath( const CString& IncludeName ) {
  CString name = ExtractFileName( IncludeName );
  CString cwd = GetCurrentDir();
  CString path = IncludeTrailingPathDelimiter( MakeNativePath( cwd ) );
  CString pathname = path + name;
  //std::cout<<"checking "<<pathname.GetCString()<<std::endl;
  if( FileExists( pathname ) ) {
    return name;
  }
  //
  for( int i = 0; i < m_IncludeDirectories.GetCount(); i++ ) {
    path = IncludeTrailingPathDelimiter( MakeNativePath( m_IncludeDirectories[i] ) );
    pathname = path + name;
    if( FileExists( pathname ) ) {
      return pathname;
    }
  }
  //std::cout<<"cannot resolve "<<IncludeName.GetCString()<<std::endl;
  return "";
}


CCppIncludeSearchFilter::CCppIncludeSearchFilter( void ) {
  //
}

CCppIncludeSearchFilter::CCppIncludeSearchFilter( const CCppIncludeSearchFilter& Filter ) :
  CIncludeSearchFilter() {
  Assign( Filter );
}

CCppIncludeSearchFilter::~CCppIncludeSearchFilter( void ) {
  //
}

void CCppIncludeSearchFilter::Assign( const CCppIncludeSearchFilter& Filter ) {
  ( void )Filter;
}

bool CCppIncludeSearchFilter::Execute( const CString& FileName, CStringList& Includes ) {
  const int STATE_UNDEFINED         = 0;
  const int STATE_ENTER_COMMENT     = 1;
  const int STATE_CHAR              = 2;
  const int STATE_STRING            = 3;
  const int STATE_BEGIN_DIRECTIVE   = 4;
  const int STATE_ONELINE_COMMENT   = 5;
  const int STATE_MULTILINE_COMMENT = 6;
  const int STATE_EXIT_COMMENT      = 7;
  const int STATE_CHAR_ESC_SEQ      = 8;
  const int STATE_STRING_ESC_SEQ    = 9;
  const int STATE_INCLUDE_DIRECTIVE = 10;
  const int STATE_INCLUDE_FILENAME  = 11;
  const int STATE_END_OF_INPUT      = -1;
  CStringList source;
  if( !source.LoadFromFile( FileName ) ) {
    return false;
  }
  if( source.GetCount() == 0 ) {
    return false;
  }
  //
  CString include_filename;
  CStringList local_includes;
  for( int l = 0; l < source.GetCount(); l++ ) {
    CString line( source.GetString( l ) );
    CStringIterator it( &line );
    int state = STATE_UNDEFINED;
    bool eol = false;
    while( !eol ) {
      switch( state ) {
        default:
        case STATE_UNDEFINED: {
          if( it.This() == '/' ) {
            state = STATE_ENTER_COMMENT;
            break;
          }
          if( it.This() == '\'' ) {
            state = STATE_CHAR;
            break;
          }
          if( it.This() == '"' ) {
            state = STATE_STRING;
            break;
          }
          if( it.This() == '#' ) {
            state = STATE_BEGIN_DIRECTIVE;
            break;
          }
          break;
        }
        case STATE_ENTER_COMMENT: {
          if( it.This() == '/' ) {
            state = STATE_ONELINE_COMMENT;
            break;
          }
          if( it.This() == '*' ) {
            state = STATE_MULTILINE_COMMENT;
            break;
          }
          break;
        }
        case STATE_ONELINE_COMMENT: {
          if( it.This() == '\0' ) {
            state = STATE_UNDEFINED;
            break;
          }
          break;
        }
        case STATE_MULTILINE_COMMENT: {
          if( it.This() == '*' ) {
            state = STATE_EXIT_COMMENT;
            break;
          }
          break;
        }
        case STATE_EXIT_COMMENT: {
          if( it.This() == '/' ) {
            state = STATE_UNDEFINED;
            break;
          }
          state = STATE_MULTILINE_COMMENT;
          break;
        }
        case STATE_CHAR: {
          if( it.This() == '\\' ) {
            state = STATE_CHAR_ESC_SEQ;
            break;
          }
          if( it.This() == '\'' ) {
            state = STATE_UNDEFINED;
            break;
          }
          break;
        }
        case STATE_CHAR_ESC_SEQ: {
          state = STATE_CHAR;
          break;
        }
        case STATE_STRING: {
          if( it.This() == '\\' ) {
            state = STATE_STRING_ESC_SEQ;
            break;
          }
          if( it.This() == '"' ) {
            state = STATE_UNDEFINED;
            break;
          }
          break;
        }
        case STATE_STRING_ESC_SEQ: {
          state = STATE_STRING;
          break;
        }
        case STATE_BEGIN_DIRECTIVE: {
          if( it.Match( "include", true ) ) {
            state = STATE_INCLUDE_DIRECTIVE;
            break;
          }
          break;
        }
        case STATE_INCLUDE_DIRECTIVE: {
          if( it.This() == '<' ) {
            state = STATE_INCLUDE_FILENAME;
            break;
          }
          if( it.This() == '"' ) {
            state = STATE_INCLUDE_FILENAME;
            break;
          }
          break;
        }
        case STATE_INCLUDE_FILENAME: {
          if( it.This() == '>' ) {
            state = STATE_UNDEFINED;
          }
          if( it.This() == '"' ) {
            state = STATE_UNDEFINED;
          }
          if( state == STATE_INCLUDE_FILENAME ) {
            include_filename.Append( it.This() );
          } else {
            local_includes.Insert( include_filename );
            include_filename.Clear();
          }
          break;
        }
        case STATE_END_OF_INPUT: {
          break;
        }
      }
      eol = ( it.This() == '\0' );
      it.Next();
    }
  }
  bool result = true;
  //ShowStringList("Local includes","include",local_includes);
  for( int i = 0; i < local_includes.GetCount(); i++ ) {
    CString resolved_include = ResolveIncludePath( local_includes[i] );
    if( !resolved_include.IsEmpty() ) {
      if( INVALID_INDEX == Includes.FindString( resolved_include ) ) {
        Includes.Insert( resolved_include );
        result = result && Execute( resolved_include, Includes );
      }
    }
  }
  Includes.RemoveDuplicates();
  return result;
}

bool CCppIncludeSearchFilter::Execute( const CString& FileName, CDependencyInfo& Dependencies ) {
  const int STATE_UNDEFINED         = 0;
  const int STATE_ENTER_COMMENT     = 1;
  const int STATE_CHAR              = 2;
  const int STATE_STRING            = 3;
  const int STATE_BEGIN_DIRECTIVE   = 4;
  const int STATE_ONELINE_COMMENT   = 5;
  const int STATE_MULTILINE_COMMENT = 6;
  const int STATE_EXIT_COMMENT      = 7;
  const int STATE_CHAR_ESC_SEQ      = 8;
  const int STATE_STRING_ESC_SEQ    = 9;
  const int STATE_INCLUDE_DIRECTIVE = 10;
  const int STATE_INCLUDE_FILENAME  = 11;
  const int STATE_END_OF_INPUT      = -1;
  //
  bool result = true;
  CString file_name = MakeNativePath( FileName );
  // check if unit is already scanned
  bool scan_unit = true;
  const size_t unit_index = Dependencies.AddRecord( file_name );
  scan_unit = !Dependencies.AreDependenciesComplete( unit_index );
  if( scan_unit ) {
    CStringList source;
    if( !source.LoadFromFile( file_name ) ) {
      return false;
    }
    if( source.GetCount() == 0 ) {
      return false;
    }
    CString include_filename;
    CStringList direct_includes;
    for( int l = 0; l < source.GetCount(); l++ ) {
      CString line( source.GetString( l ) );
      CStringIterator it( &line );
      int state = STATE_UNDEFINED;
      bool eol = false;
      while( !eol ) {
        switch( state ) {
          default:
          case STATE_UNDEFINED: {
            if( it.This() == '/' ) {
              state = STATE_ENTER_COMMENT;
              break;
            }
            if( it.This() == '\'' ) {
              state = STATE_CHAR;
              break;
            }
            if( it.This() == '"' ) {
              state = STATE_STRING;
              break;
            }
            if( it.This() == '#' ) {
              state = STATE_BEGIN_DIRECTIVE;
              break;
            }
            break;
          }
          case STATE_ENTER_COMMENT: {
            if( it.This() == '/' ) {
              state = STATE_ONELINE_COMMENT;
              break;
            }
            if( it.This() == '*' ) {
              state = STATE_MULTILINE_COMMENT;
              break;
            }
            break;
          }
          case STATE_ONELINE_COMMENT: {
            if( it.This() == '\0' ) {
              state = STATE_UNDEFINED;
              break;
            }
            break;
          }
          case STATE_MULTILINE_COMMENT: {
            if( it.This() == '*' ) {
              state = STATE_EXIT_COMMENT;
              break;
            }
            break;
          }
          case STATE_EXIT_COMMENT: {
            if( it.This() == '/' ) {
              state = STATE_UNDEFINED;
              break;
            }
            state = STATE_MULTILINE_COMMENT;
            break;
          }
          case STATE_CHAR: {
            if( it.This() == '\\' ) {
              state = STATE_CHAR_ESC_SEQ;
              break;
            }
            if( it.This() == '\'' ) {
              state = STATE_UNDEFINED;
              break;
            }
            break;
          }
          case STATE_CHAR_ESC_SEQ: {
            state = STATE_CHAR;
            break;
          }
          case STATE_STRING: {
            if( it.This() == '\\' ) {
              state = STATE_STRING_ESC_SEQ;
              break;
            }
            if( it.This() == '"' ) {
              state = STATE_UNDEFINED;
              break;
            }
            break;
          }
          case STATE_STRING_ESC_SEQ: {
            state = STATE_STRING;
            break;
          }
          case STATE_BEGIN_DIRECTIVE: {
            if( it.Match( "include", true ) ) {
              state = STATE_INCLUDE_DIRECTIVE;
              break;
            }
            break;
          }
          case STATE_INCLUDE_DIRECTIVE: {
            if( it.This() == '<' ) {
              state = STATE_INCLUDE_FILENAME;
              break;
            }
            if( it.This() == '"' ) {
              state = STATE_INCLUDE_FILENAME;
              break;
            }
            break;
          }
          case STATE_INCLUDE_FILENAME: {
            if( it.This() == '>' )
            { state = STATE_UNDEFINED; }
            if( it.This() == '"' )
            { state = STATE_UNDEFINED; }
            if( state == STATE_INCLUDE_FILENAME )
            { include_filename.Append( it.This() ); }
            else {
              direct_includes.Insert( include_filename );
              include_filename.Clear();
            }
            break;
          }
          case STATE_END_OF_INPUT: {
            break;
          }
        }
        eol = ( it.This() == '\0' );
        it.Next();
      }
    }
    Dependencies.SetDependenciesComplete( unit_index );
    int resolved_count = 0;
    for( int i = 0; i < direct_includes.GetCount(); i++ ) {
      CString resolved_include = ResolveIncludePath( direct_includes[i] );
      if( !resolved_include.IsEmpty() ) {
        Dependencies.AddDependency( unit_index, resolved_include );
        resolved_count++;
        int resolved_index = Dependencies.FindRecord( resolved_include );
        if( !Dependencies.AreDependenciesComplete( resolved_index ) ) {
          result = result && Execute( resolved_include, Dependencies );
        }
      }
    }
  }
  return result;
}

//------------------------------------------------------------------------------
